-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Add support for tracking arbitrary events #136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
private flagEvaluationDetailsBuilder(flagKey: string): FlagEvaluationDetailsBuilder { | ||
// noinspection JSUnusedGlobalSymbols | ||
track(event: unknown, params: Record<string, unknown>) { | ||
this.eventQueue.push({ event, params }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this implementation to be fleshed out in future PRs
private isObfuscated = false, | ||
) {} | ||
constructor({ | ||
eventQueue = new ArrayBackedNamedEventQueue('events'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the named parameters.
🤔 I do NOT consider this a breaking change as users are not using this constructor directly; they are going through init
. Am I misunderstanding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's correct sir!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to strict semver, this does require a major version bump, however, as you pointed out, since it's only used internally, it doesn't affect end users
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, was worried about this as well
let result: ContextAttributes; | ||
if (this.isInstanceOfContextualAttributes(subjectAttributes)) { | ||
result = subjectAttributes as ContextAttributes; | ||
return subjectAttributes as ContextAttributes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice - agreed
} else { | ||
// If no logger defined, queue up the events (up to a max) to flush if a logger is later defined | ||
this.queuedBanditEvents.push(banditEvent); | ||
this.banditEventsQueue.push(banditEvent); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no limit anymore ya?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the limit is now encapsulated inside the logic of BoundedEventQueue#push
method!
} | ||
|
||
private logAssignment(result: FlagEvaluation) { | ||
private maybeLogAssignment(result: FlagEvaluation) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha - true
export class BoundedEventQueue<T> { | ||
constructor( | ||
private readonly queue: NamedEventQueue<T>, | ||
private readonly maxSize = MAX_EVENT_QUEUE_SIZE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the bandit event queue above and makes sense now 👍
|
||
try { | ||
if (this.assignmentLogger) { | ||
this.assignmentLogger.logAssignment(event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you considering any more major changes to the SDK behavior, such as all events getting put into a queue and processed by another worker?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, we'll need some mechanism for batching and uploading events. I'm thinking a simple setInterval
logic that uploads every N seconds, no need to overcomplicate. Thoughts?
|
||
/** A named event queue backed by an array. */ | ||
export default class ArrayBackedNamedEventQueue<T> implements NamedEventQueue<T> { | ||
private readonly events: T[] = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having an unbounded memory-based array sounds risky. Maybe this should be limited to a certain size if/until we support spillover to disk?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see there is a separate class for limiting the size. Could we combine them to simplify and avoid the risk of someone using this class directly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this class was not meant to be used directly, only internally from BoundedEventQueue
via composition. I'll add a comment to make that clear, good callout
Towards: FF-3567
Motivation and Context
Laying the groundwork for event ingestion and logging
Description
NamedEventQueue
abstraction, to be shared between assignment, bandit and arbitrary application events.EppoClient
constructor to use props object pattern with optional argsSdkKeyDecoder
to be used in follow up PRsThe js-server-sdk and js-client-sdk will have separate implementations for
NamedEventQueue
, one based on file storage and the other based on localstorage. I already have both implemented, will submit those as follow up PRs.Event batching, uploading, etc. will be handled in follow up PRs as well.
How has this been tested?
Existing tests